package com.xhl.shiro.config;

import com.xhl.shiro.cache.ShiroRedisProperties;
import com.xhl.shiro.cache.RedisSessionDao;
import com.xhl.shiro.oauth2.OAuth2Filter;
import com.xhl.shiro.oauth2.OAuth2Realm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @ClassName: MyShiroConfig
 * @Description: shiro 配置
 * @Author:     lisk
 * @Date:        2023-08-18 15:34
 * @Version: 1.0
 */
@Configuration
@EnableConfigurationProperties({ShiroRedisProperties.class})
public class MyShiroConfig {

    @Bean("securityManager")
    public SecurityManager securityManager(OAuth2Realm oAuth2Realm,CacheManager cacheManager,  SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(oAuth2Realm);
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setCacheManager(cacheManager);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }


    @Bean
    public RememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        //设置cookie名称，对应login.html页面的<input type="checkbox" name="rememberMe"/>
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //防止某些攻击，不被Javascript代码获取
        simpleCookie.setHttpOnly(true);
        //单位为秒，设置cookie过期时间
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);
        //设置Cookie
        rememberMeManager.setCookie(simpleCookie);
        //设置"Remember Me"功能中使用的密钥的代码,使用Base64编码字符串解码得到字节数组
        rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return rememberMeManager;
    }


    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new OAuth2Filter());
        shiroFilter.setFilters(filters);

        //认证过滤器
        //anon：无需认证。
        //authc：必须认证。
        //authcBasic：需要通过HTTPBasic认证。
        //user：不一定通过认证，只要曾经被Shiro记录即可，比如：记住我

        //授权过滤器
        //perms：必须拥有某个权限才能访问。
        //role：必须拥有某个角色才能访问。
        //port：请求的端口必须是指定值才可以。
        //rest：请求必须基于RESTful，POST，PUT，GET，DELETE。
        //ssl：必须是安全的URL请求，协议HTTP
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/druid/**", "anon");
        filterMap.put("/app/**", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/aaa.txt", "anon");
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    @ConditionalOnWebApplication
    public SessionManager sessionManager(SessionDAO sessionDAO, CacheManager cacheManager,ShiroRedisProperties shiroRedisProperties) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO);
        long time = shiroRedisProperties.getSessionTimeOut();
        sessionManager.setGlobalSessionTimeout(time > 0 ? time : shiroRedisProperties.MILLIS_PER_MINUTE * 30);
        sessionManager.setCacheManager(cacheManager);
        return sessionManager;
    }

    @Bean
    @ConditionalOnWebApplication
    public SessionDAO sessionDAO(RedisTemplate<Object, Object> redisTemplate, ShiroRedisProperties shiroRedisProperties) {
        return new RedisSessionDao(shiroRedisProperties.getSessionPrefix(),
                shiroRedisProperties.getSessionCacheExpire(),
                redisTemplate);
    }
}
